#if 0
Copyright 2011 Google. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of Google.
#endif

#include <assert.h>
#include <malloc.h>
#include <new>
#include <stdio.h>

#include "win32_allocator_interface.h"
#include "win32_allocator_patcher.h"

#ifndef DCHECK
# define DCHECK(X) assert((X));
#endif

namespace alloc_override {

#define DEBUG_OVERRIDE(BOOL_PTR)                                             \
  *BOOL_PTR = true;                                                          \
  printf("Override allocator function \"%s\" called\n", __FUNCTION__);

// Win32AllocatorInterface instance that is suitable for testing.
class TestWin32Allocator : public Win32AllocatorInterface {
 public:
  struct Bits {
    bool malloc_called;
    bool calloc_called;
    bool free_called;
    bool realloc_called;
    bool expand_called;
    bool msize_called;
    bool new_called;
    bool new_no_throw_called;
    bool new_array_called;
    bool new_array_no_throw_called;
    bool delete_called;
    bool delete_array_called;
    bool strdup_called;
  };

  TestWin32Allocator() { ResetBitField(); }
  Bits& bits() { return bits_; }

  void ResetBitField() {
    memset(&bits_, 0, sizeof(bits_));
  }

  virtual void* Malloc(size_t sz) {
    DEBUG_OVERRIDE(&bits().malloc_called);
    return RawWin32SystemAllocator().Malloc(sz);
  }
  virtual void* Calloc(size_t num, size_t sz) {
    DEBUG_OVERRIDE(&bits().calloc_called);
    return RawWin32SystemAllocator().Calloc(num, sz);
  }
  virtual void* Realloc(void* ptr, size_t sz) {
    DEBUG_OVERRIDE(&bits().realloc_called);
    return RawWin32SystemAllocator().Realloc(ptr, sz);
  }
  virtual void* Expand(void* memblock, size_t sz) {
    DEBUG_OVERRIDE(&bits().expand_called);
    return RawWin32SystemAllocator().Expand(memblock, sz);
  }
  virtual char* Strdup(const char* str) {
    DEBUG_OVERRIDE(&bits().strdup_called);
    return RawWin32SystemAllocator().Strdup(str);
  }
  virtual size_t Msize(void* memblock) {
    DEBUG_OVERRIDE(&bits().msize_called);
    return RawWin32SystemAllocator().Msize(memblock);
  }
  virtual void Free(void* ptr) {
    DEBUG_OVERRIDE(&bits().free_called);
    RawWin32SystemAllocator().Free(ptr);
  }

  virtual void* New(std::size_t size) {
    DEBUG_OVERRIDE(&bits().new_called);
    return RawWin32SystemAllocator().New(size);
  }
  virtual void* NewNoThrow(std::size_t size) {
    DEBUG_OVERRIDE(&bits().new_no_throw_called);
    return RawWin32SystemAllocator().NewNoThrow(size);
  }
  virtual void* NewArray(std::size_t size) {
    DEBUG_OVERRIDE(&bits().new_array_called);
    return RawWin32SystemAllocator().NewArray(size);
  }
  virtual void* NewArrayNoThrow(std::size_t size) {
    DEBUG_OVERRIDE(&bits().new_array_no_throw_called);
    return RawWin32SystemAllocator().NewArrayNoThrow(size);
  }
  virtual void Delete(void* ptr) {
    DEBUG_OVERRIDE(&bits().delete_called);
    RawWin32SystemAllocator().Delete(ptr);
  }
  virtual void DeleteArray(void* ptr) {
    DEBUG_OVERRIDE(&bits().delete_array_called);
    RawWin32SystemAllocator().DeleteArray(ptr);
  }
 private:
  Bits bits_;
};
#undef DEBUG_OVERRIDE
}  // namespace alloc_override

int main(char** argv, int argc) {
  using namespace alloc_override;

  // Intentionally omit destruction of the allocator. Otherwise
  // when the program exits, deallocations will go through a dead
  // allocator.
  TestWin32Allocator* s_test_allocator = new TestWin32Allocator;

  OverrideAllocatorOnce(s_test_allocator);

  void* malloc_ptr = malloc(5);
  DCHECK(s_test_allocator->bits().malloc_called);
  free(malloc_ptr);
  DCHECK(s_test_allocator->bits().free_called);

  malloc_ptr = malloc(5);
  malloc_ptr = realloc(malloc_ptr, 10);
  DCHECK(s_test_allocator->bits().realloc_called);
  free(malloc_ptr);

  malloc_ptr = calloc(4, 5);
  DCHECK(s_test_allocator->bits().calloc_called);
  free(malloc_ptr);

  malloc_ptr = malloc(5);
  _expand(malloc_ptr, 10);
  DCHECK(s_test_allocator->bits().expand_called);
  free(malloc_ptr);

  malloc_ptr = malloc(5);
  size_t block_size = _msize(malloc_ptr);
  DCHECK(s_test_allocator->bits().msize_called);
  DCHECK(block_size >= 5);

  int* new_ptr = new int;
  DCHECK(s_test_allocator->bits().new_called);
  delete(new_ptr);
  DCHECK(s_test_allocator->bits().delete_called);

  new_ptr = new (std::nothrow) int;
  DCHECK(s_test_allocator->bits().new_no_throw_called);
  delete new_ptr;

  new_ptr = new int[4];
  DCHECK(s_test_allocator->bits().new_array_called);
  delete[] new_ptr;
  DCHECK(s_test_allocator->bits().delete_array_called);

  new_ptr = new(std::nothrow) int[4];
  DCHECK(s_test_allocator->bits().new_array_no_throw_called);
  delete[] new_ptr;

  free(_strdup("dummy_str"));
  DCHECK(s_test_allocator->bits().strdup_called);

  return 0;
}
